#include "stdafx.h"
#include <utils/ErrorMessage.h>
#include "../sgp/Settings.h"
#include "DXCommon.h"
#include "../sgp/VideoSurfaceLock.h"
#include "../sgp/VideoManager.h"

namespace ja2{ namespace sgp
{
	VideoManager::VideoManager(HWND hWnd, Settings &Settings_)
		: m_ScreenWidth(Settings_.res_width()),
		m_ScreenHeight(Settings_.res_height()),
		m_PixelDepth(Settings_.m_PixelDepth),
		m_hWnd(hWnd), 
		m_FrameBufferState(BS_BUFFER_DIRTY),
		m_MouseBufferState(BS_BUFFER_DISABLED),
		m_VideoState(VS_ON),
		m_ForceFullScreenRefresh(true),
		m_Settings(Settings_)
	{
		
		// Register debug topics
		JA2_REGISTER_DEBUG_TOPIC(TOPIC_VIDEO, "Video");
		JA2_DEBUG_MESSAGE(TOPIC_VIDEO, DebugManager::DL_0, "Initializing the video manager");
		// Create the Direct Draw Object
		dx_attempt(DirectDrawCreate(NULL, &m_pDirectDrawObjectRef, NULL));
		// Get the right interface
		dx_attempt(m_pDirectDrawObjectRef.QueryInterface<IDirectDraw7>(IID_IDirectDraw7, &m_pDirectDraw));
		// Set the exclusive mode
		dx_attempt(m_pDirectDraw->SetCooperativeLevel(m_hWnd, (m_Settings.is_full_screen() ? DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN : DDSCL_NORMAL)));
		// Set the display mode if full screen
		if(m_Settings.is_full_screen())
			dx_attempt(m_pDirectDraw->SetDisplayMode(m_Settings.res_width(), m_Settings.res_height(), m_Settings.m_PixelDepth, 0, 0 ));
		// First do Pixel Format
		DDPIXELFORMAT pixel_format = DDPIXELFORMAT();
		pixel_format.dwSize = sizeof( pixel_format );
		pixel_format.dwFlags = DDPF_RGB;
		// BPP always 32
		pixel_format.dwRGBBitCount = 32;
		pixel_format.dwRBitMask = 0x00FF0000;
		pixel_format.dwGBitMask  = 0x0000FF00;
		pixel_format.dwBBitMask  = 0x000000FF;
		// Initialize Primary Surface along with BackBuffer
		DDSURFACEDESC2 surface_description = DDSURFACEDESC2();
		IDirectDrawSurfacePtr p_primary_surface;
		IDirectDrawSurfacePtr p_back_buffer;
		// Windowed mode
		if(!m_Settings.is_full_screen())
		{
			// Create a primary surface
			surface_description.dwSize = sizeof(surface_description);
			surface_description.dwFlags = DDSD_CAPS;
			surface_description.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
			dx_attempt(m_pDirectDraw->CreateSurface(&surface_description, &p_primary_surface, NULL));
			// Set clipper to surface
			// Create clipper
			IDirectDrawClipperPtr p_clipper;
			dx_attempt(m_pDirectDraw->CreateClipper(0, &p_clipper, NULL));
			dx_attempt(p_clipper->SetHWnd(0, m_hWnd));
			dx_attempt(p_primary_surface->SetClipper(p_clipper));
			// Create back buffer
			surface_description = DDSURFACEDESC2();
			surface_description.dwSize = sizeof(surface_description);
			surface_description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
			surface_description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
			surface_description.dwWidth = m_Settings.res_width();
			surface_description.dwHeight = m_Settings.res_height();
			dx_attempt(m_pDirectDraw->CreateSurface(&surface_description, &p_back_buffer, NULL ));
		}
		// Fullscreen
		else
		{
			// Create primary surface
			surface_description.dwSize = sizeof(DDSURFACEDESC);
			surface_description.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
			surface_description.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
			surface_description.dwBackBufferCount = 1;
			dx_attempt(m_pDirectDraw->CreateSurface(&surface_description, &p_primary_surface, NULL ));
			// Create back buffer
			DDSCAPS2 surface_caps;
			surface_caps.dwCaps = DDSCAPS_BACKBUFFER;
			dx_attempt(p_primary_surface->GetAttachedSurface(&surface_caps, &p_back_buffer));
		}
		m_pBackBufferSurface = create_surface_from_dd_surface(p_back_buffer);
		m_pPrimarySurface = create_surface_from_dd_surface(p_primary_surface);
		// Initialize the frame buffer
		IDirectDrawSurfacePtr p_frame_buffer;
		surface_description = DDSURFACEDESC2();
		surface_description.dwSize = sizeof(surface_description);
		surface_description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
		surface_description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
		surface_description.dwWidth = m_Settings.res_width();
		surface_description.dwHeight = m_Settings.res_height();
		surface_description.ddpfPixelFormat = pixel_format;
		dx_attempt(m_pDirectDraw->CreateSurface(&surface_description, &p_frame_buffer, NULL ));
		// Blank out the frame buffer
		{
			VideoSurfaceLock lock(p_frame_buffer);
			memset(lock.buffer(), 50, m_Settings.res_height() * lock.pitch());
		}
		m_pFrameBufferSurface = create_surface_from_dd_surface(p_frame_buffer);
		// Initialize the main mouse original surface
		surface_description = DDSURFACEDESC2();
		surface_description.dwSize = sizeof(surface_description);
		surface_description.dwFlags	= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
		surface_description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
		surface_description.dwWidth	= MAX_CURSOR_WIDTH;
		surface_description.dwHeight = MAX_CURSOR_HEIGHT;
		{
			HRESULT const ret = m_pDirectDraw->CreateSurface(&surface_description, &m_pMouseCursorOriginalDD, NULL);
			if(ret != DD_OK)
			{
				JA2_DEBUG_MESSAGE(TOPIC_VIDEO, DebugManager::DL_0, (boost::format(resources::FAILED_TO_CREATE_MOUSE_CURSOR) % (ret & 0x0f)).str().c_str());
				dx_attempt(ret);
			}
		}
		// Initialize the main mouse surfaces as duplicate
		IDirectDrawSurfacePtr p_mouse_cursor;
		{
			HRESULT const ret = m_pDirectDraw->DuplicateSurface(m_pMouseCursorOriginalDD, &p_mouse_cursor);
			if(ret != DD_OK)
			{
				JA2_DEBUG_MESSAGE(TOPIC_VIDEO, DebugManager::DL_0, resources::FAILED_TO_CREATE_MOUSE_CURSOR_ORIG);
				dx_attempt(ret);
			}
		}
		// Set color key for mouse surface
		DDCOLORKEY color_key;
		color_key.dwColorSpaceLowValue = 0;
		color_key.dwColorSpaceHighValue = 0;
		dx_attempt(p_mouse_cursor->SetColorKey(DDCKEY_SRCBLT, &color_key));
		m_pMouseBufferSurface = create_surface_from_dd_surface(p_mouse_cursor);
		// Initialize the main mouse background surfaces. There are two of them (one for each of the Primary and Backbuffer surfaces)
		m_pMouseCursorCurrent.reset(new MouseCursorBackground(m_pDirectDraw.GetInterfacePtr()));
		m_pMouseCursorPrevious.reset(new MouseCursorBackground(m_pDirectDraw.GetInterfacePtr()));
		// Initialize the mutex sections
		// ATE: Keep these mutexes for now!
//		if (InitializeMutex(REFRESH_THREAD_MUTEX, (UINT8 *)"RefreshThreadMutex") == FALSE)
//		{
//			return FALSE;
//		}
//		if (InitializeMutex(FRAME_BUFFER_MUTEX, (UINT8 *)"FrameBufferMutex") == FALSE)
//		{
//			return FALSE;
//		}
//		if (InitializeMutex(MOUSE_BUFFER_MUTEX, (UINT8 *)"MouseBufferMutex") == FALSE)
//		{
//			return FALSE;
//		}
		// Initialize state variables


//		guiRefreshThreadState		= THREAD_OFF;
//		guiDirtyRegionCount			= 0;
//x		gpFrameBufferRefreshOverride = NULL;
//x		gpCursorStore				= NULL;
//x		gfPrintFrameBuffer			= FALSE;
//x		guiPrintFrameBufferIndex	 = 0;

		// This function must be called to setup RGB information
//x		if (GetRGBDistribution() == FALSE)
//x			return FALSE;
	}

	VideoManager::~VideoManager()
	{
		JA2_DEBUG_MESSAGE(TOPIC_VIDEO, DebugManager::DL_0, resources::VIDEO_MANAGER_SHUTTING_DOWN);
		// Destroy mouse cursor bkgnd
		m_pMouseCursorCurrent.reset();
		m_pMouseCursorPrevious.reset();
		// Restore display mode
		m_pDirectDraw->RestoreDisplayMode();
		m_pDirectDraw->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL);

//x		if (gpCursorStore != NULL)
//x		{
//x			DeleteVideoObject(gpCursorStore);
//x			gpCursorStore = NULL;
//x		}
		// ATE: Release mouse cursor!
//x		FreeMouseCursor( );
		JA2_UNREGISTER_DEBUG_TOPIC(TOPIC_VIDEO, "Video");
	}

	void VideoManager::invalidate_screen()
	{
		// Clear all dirty region because whole screen is to be redrawn
		m_DirtyRegions.clear();
		m_ForceFullScreenRefresh = true;
		// Set buffer state
		m_FrameBufferState = BS_BUFFER_DIRTY;
	}

	void VideoManager::refresh_screen()
	{
#if 0
		static UINT32	uiRefreshThreadState, uiIndex;
		UINT16	usScreenWidth, usScreenHeight;
		HRESULT ReturnCode;
		static RECT	Region;
		static INT16	sx, sy;
		UINT32						uiTime;

		usScreenWidth = usScreenHeight = 0;

		// Should mose be shown
		static bool show_mouse = false;
		
		if( gfNextRefreshFullScreen )
		{
			if( guiCurrentScreen == GAME_SCREEN )
			{
				InvalidateScreen();
				gfRenderScroll = FALSE;
				//			gfForceFullScreenRefresh = TRUE;
				//			guiFrameBufferState == BUFFER_DIRTY;
			}
			gfNextRefreshFullScreen = FALSE;
		}

		//DebugMsg(TOPIC_VIDEO, DBG_LEVEL_0, "Looping in refresh");

		///////////////////////////////////////////////////////////////////////////////////////////////
		//
		// REFRESH_THREAD_MUTEX
		//
		///////////////////////////////////////////////////////////////////////////////////////////////

		switch(m_VideoState)
		{
			// Everything is OK
		case VS_ON:
			uiRefreshThreadState = guiRefreshThreadState = THREAD_ON;
			usScreenWidth = gusScreenWidth;
			usScreenHeight = gusScreenHeight;
			break;
			// Hot damn, the video manager is suddenly off. We have to bugger out of here. Don't forget to
			// leave the critical section
		case VS_OFF:
			guiRefreshThreadState = THREAD_OFF;
			return;
			// This are suspended. Make sure the refresh function does try to access any of the direct draw surfaces
		case VS_SUSPENDED:
			uiRefreshThreadState = guiRefreshThreadState = THREAD_SUSPENDED;
			break;
			// Well things are shutting down. So we need to bugger out of there. Don't forget to leave the
			// critical section before returning
		case VS_SHUTTING_DOWN:
			guiRefreshThreadState = THREAD_OFF;
			return;
		}

		/////////////////////////////////////////////////////////////////////////////////////////////
		//
		// FRAME_BUFFER_MUTEX
		//
		/////////////////////////////////////////////////////////////////////////////////////////////


		// RESTORE OLD POSITION OF MOUSE
		if (m_pMouseCursorCurrent->restore())
		{
//			Region.left = gMouseCursorBackground[CURRENT_MOUSE_DATA].usLeft;
//			Region.top = gMouseCursorBackground[CURRENT_MOUSE_DATA].usTop;
//			Region.right = gMouseCursorBackground[CURRENT_MOUSE_DATA].usRight;
//			Region.bottom = gMouseCursorBackground[CURRENT_MOUSE_DATA].usBottom;
//
//			do
//			{
//				ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseXPos, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseYPos, gMouseCursorBackground[CURRENT_MOUSE_DATA].pSurface, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
//				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
//				{
//					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
//
//					if (ReturnCode == DDERR_SURFACELOST)
//					{
//						goto ENDOFLOOP;
//					}
//				}
//			} while (ReturnCode != DD_OK);
			m_pMouseCursorCurrent->blit(m_pBackBuffer);
			// Save position into other background region
			m_pMouseCursorPrevious->update(*m_pMouseCursorCurrent);
//			memcpy( &(gMouseCursorBackground[PREVIOUS_MOUSE_DATA] ), &(gMouseCursorBackground[CURRENT_MOUSE_DATA] ), sizeof( MouseCursorBackground ) );
		}

#endif
		// Ok we were able to get a hold of the frame buffer stuff. Check to see if it needs updating
		// if not, release the frame buffer stuff right away
		if (m_FrameBufferState == BS_BUFFER_DIRTY)
		{
/*
			// Well the frame buffer is dirty.
			if (gpFrameBufferRefreshOverride != NULL)
			{
				//
				// Method (3) - We are using a function override to refresh the frame buffer. First we
				// call the override function then we must set the override pointer to NULL
				//

				(*gpFrameBufferRefreshOverride)();
				gpFrameBufferRefreshOverride = NULL;

			}
			if ( gfFadeInitialized && gfFadeInVideo )
				gFadeFunction( );
			// Either Method (1) or (2)
			else
			{
*/
				if(m_ForceFullScreenRefresh)
				{
					// Method (1) - We will be refreshing the entire screen
//					RECT region = {0, 0, m_Settings.res_width(), m_Settings.res_height()};
//					dx_attempt(m_pBackBuffer->BltFast(0, 0, m_pFrameBuffer, &region, DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT));
					m_pBackBufferSurface->blit(*m_pFrameBufferSurface, 0, 0, VideoSurface::BF_FAST, nullptr);
				}
/*				else
				{

//					for (uiIndex = 0; uiIndex < guiDirtyRegionCount; uiIndex++)
					for(auto it = m_DirtyRegions.begin(), end = m_DirtyRegions.end(); it != end; ++it)
					{
						Region.left	= gListOfDirtyRegions[uiIndex].iLeft;
						Region.top	= gListOfDirtyRegions[uiIndex].iTop;
						Region.right	= gListOfDirtyRegions[uiIndex].iRight;
						Region.bottom = gListOfDirtyRegions[uiIndex].iBottom;

						do
						{
							ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, Region.left, Region.top, gpFrameBuffer, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
							if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
							{
								DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
							}

							if (ReturnCode == DDERR_SURFACELOST)
							{
								goto ENDOFLOOP;
							}
						} while (ReturnCode != DD_OK);
					}
					// Now do new, extended dirty regions
					for (uiIndex = 0; uiIndex < guiDirtyRegionExCount; uiIndex++)
					{
						Region.left	= gDirtyRegionsEx[uiIndex].iLeft;
						Region.top	= gDirtyRegionsEx[uiIndex].iTop;
						Region.right	= gDirtyRegionsEx[uiIndex].iRight;
						Region.bottom = gDirtyRegionsEx[uiIndex].iBottom;

						// Do some checks if we are in the process of scrolling!
						if ( gfRenderScroll )
						{

							// Check if we are completely out of bounds
							if ( Region.top <= gsVIEWPORT_WINDOW_END_Y	&& Region.bottom <= gsVIEWPORT_WINDOW_END_Y )
							{
								continue;
							}

						}

						do
						{
							ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, Region.left, Region.top, gpFrameBuffer, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
							if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
							{
								DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
							}

							if (ReturnCode == DDERR_SURFACELOST)
							{
								goto ENDOFLOOP;
							}
						} while (ReturnCode != DD_OK);
					}

				}

			}
			if ( gfRenderScroll )
			{
				//			ScrollJA2Background( guiScrollDirection, gsScrollXIncrement, gsScrollYIncrement, gpPrimarySurface, gpBackBuffer, TRUE, PREVIOUS_MOUSE_DATA );
				ScrollJA2Background( guiScrollDirection, gsScrollXIncrement, gsScrollYIncrement, gpBackBuffer, gpBackBuffer, TRUE, PREVIOUS_MOUSE_DATA );
			}

			gfIgnoreScrollDueToCenterAdjust = FALSE;

			//
			// Update the guiFrameBufferState variable to reflect that the frame buffer can now be handled
			//
			guiFrameBufferState = BUFFER_READY;
*/
		}
#if 0
		//
		// Do we want to print the frame stuff ??
		//

		if( gfVideoCapture )
		{
			uiTime=GetTickCount();
			if((uiTime < guiLastFrame) || (uiTime > (guiLastFrame+guiFramePeriod)))
			{
				SnapshotSmall( );
				guiLastFrame=uiTime;
			}
		}


		if (gfPrintFrameBuffer == TRUE)
		{
			LPDIRECTDRAWSURFACE	_pTmpBuffer;
			LPDIRECTDRAWSURFACE2	pTmpBuffer;
			DDSURFACEDESC			SurfaceDescription;
#ifndef USE_VFS
			FILE					*OutputFile;
#endif
			CHAR8					FileName[64];
			INT32					iIndex;
			UINT16				 *p16BPPData;

			// Snap: save current directory
#ifndef USE_VFS
			STRING512			DataDir;
			STRING512			ExecDir;

			GetFileManCurrentDirectory( DataDir );
			GetExecutableDirectory( ExecDir );
			SetFileManCurrentDirectory( ExecDir );
#endif
			//
			// Create temporary system memory surface. This is used to correct problems with the backbuffer
			// surface which can be interlaced or have a funky pitch
			//

			ZEROMEM(SurfaceDescription);
			SurfaceDescription.dwSize		 = sizeof(DDSURFACEDESC);
			SurfaceDescription.dwFlags		= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
			SurfaceDescription.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
			SurfaceDescription.dwWidth		= SCREEN_WIDTH;
			SurfaceDescription.dwHeight		= SCREEN_HEIGHT;
			ReturnCode = IDirectDraw2_CreateSurface ( gpDirectDrawObject, &SurfaceDescription, &_pTmpBuffer, NULL );
			if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
			{
				DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
			}

			ReturnCode = IDirectDrawSurface_QueryInterface(_pTmpBuffer, /*&*/IID_IDirectDrawSurface2, (LPVOID *)&pTmpBuffer); // (jonathanl)
			if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
			{
				DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
			}

			//
			// Copy the primary surface to the temporary surface
			//

			Region.left = 0;
			Region.top = 0;
			Region.right = SCREEN_WIDTH;
			Region.bottom = SCREEN_HEIGHT;

			do
			{
				ReturnCode = IDirectDrawSurface2_SGPBltFast(pTmpBuffer, 0, 0, gpPrimarySurface, &rcWindow, DDBLTFAST_NOCOLORKEY);
				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
				}
			} while (ReturnCode != DD_OK);

			//
			// Ok now that temp surface has contents of backbuffer, copy temp surface to disk
			//

			do
			{
				sprintf( FileName, "SCREEN%03d.TGA", guiPrintFrameBufferIndex++);
			}
#ifndef USE_VFS
			while( (_access( FileName, 0 )) != -1 );
#else
			while(FileExists(FileName));
#endif

#ifndef USE_VFS
			if ((OutputFile = fopen( FileName, "wb")) != NULL)
			{
				fprintf(OutputFile, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, LOBYTE(SCREEN_WIDTH), HIBYTE(SCREEN_WIDTH), LOBYTE(SCREEN_HEIGHT), HIBYTE(SCREEN_HEIGHT), 0x10, 0);
#else
			try
			{
				vfs::COpenWriteFile wfile(FileName,true,true);
				char head[] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, LOBYTE(SCREEN_WIDTH), HIBYTE(SCREEN_WIDTH), LOBYTE(SCREEN_HEIGHT), HIBYTE(SCREEN_HEIGHT), 0x10, 0};
				TRYCATCH_RETHROW(wfile.file().write(head,18), L"");
#endif

				//
				// Lock temp surface
				//

				ZEROMEM(SurfaceDescription);
				SurfaceDescription.dwSize = sizeof(DDSURFACEDESC);
				ReturnCode = IDirectDrawSurface2_Lock(pTmpBuffer, NULL, &SurfaceDescription, 0, NULL);
				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
				}

				//
				// Copy 16 bit buffer to file
				//

				// 5/6/5.. create buffer...
				if (gusRedMask == 0xF800 && gusGreenMask == 0x07E0 && gusBlueMask == 0x001F)
				{
					p16BPPData = (UINT16 *)MemAlloc( SCREEN_WIDTH * 2 );
				}

				for (iIndex = SCREEN_HEIGHT - 1; iIndex >= 0; iIndex--)
				{
					// ATE: OK, fix this such that it converts pixel format to 5/5/5
					// if current settings are 5/6/5....
					if (gusRedMask == 0xF800 && gusGreenMask == 0x07E0 && gusBlueMask == 0x001F)
					{
						// Read into a buffer...
						memcpy( p16BPPData, ( ((UINT8 *)SurfaceDescription.lpSurface) + (iIndex * SCREEN_WIDTH * 2) ), SCREEN_WIDTH * 2 );

						// Convert....
						ConvertRGBDistribution565To555( p16BPPData, SCREEN_WIDTH );

						// Write
#ifndef USE_VFS
						fwrite( p16BPPData, SCREEN_WIDTH * 2, 1, OutputFile);
#else
						TRYCATCH_RETHROW(wfile.file().write((vfs::Byte*)p16BPPData, SCREEN_WIDTH * 2), L"");
#endif
					}
					else
					{
#ifndef USE_VFS
						fwrite((void *)(((UINT8 *)SurfaceDescription.lpSurface) + (iIndex * SCREEN_WIDTH * 2)), SCREEN_WIDTH * 2, 1, OutputFile);
#else
						TRYCATCH_RETHROW(wfile.file().write((vfs::Byte*)(((UINT8 *)SurfaceDescription.lpSurface) + (iIndex * SCREEN_WIDTH * 2)), SCREEN_WIDTH * 2), L"");
#endif
					}
				}

				// 5/6/5.. Delete buffer...
				if (gusRedMask == 0xF800 && gusGreenMask == 0x07E0 && gusBlueMask == 0x001F)
				{
					MemFree( p16BPPData );
				}
#ifndef USE_VFS
				fclose(OutputFile);
#endif
				//
				// Unlock temp surface
				//

				ZEROMEM(SurfaceDescription);
				SurfaceDescription.dwSize = sizeof(DDSURFACEDESC);
				ReturnCode = IDirectDrawSurface2_Unlock(pTmpBuffer, &SurfaceDescription);
				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
				}
			}

#ifdef USE_VFS
			catch(CBasicException& ex)
			{
				RETHROWEXCEPTION(L"",&ex);
			}
#endif

			//
			// Release temp surface
			//

			gfPrintFrameBuffer = FALSE;
			IDirectDrawSurface2_Release(pTmpBuffer);
#ifndef USE_VFS
			// Snap: Restore the data directory once we are finished.
			SetFileManCurrentDirectory( DataDir );
			//strcat( ExecDir, "\\Data" );
			//SetFileManCurrentDirectory( ExecDir );
#endif
		}

		//
		// Ok we were able to get a hold of the frame buffer stuff. Check to see if it needs updating
		// if not, release the frame buffer stuff right away
		//

		if (guiMouseBufferState == BUFFER_DIRTY)
		{
			//
			// Well the mouse buffer is dirty. Upload the whole thing
			//

			Region.left = 0;
			Region.top = 0;
			Region.right = gusMouseCursorWidth;
			Region.bottom = gusMouseCursorHeight;

			do
			{
				ReturnCode = IDirectDrawSurface2_SGPBltFast(gpMouseCursor, 0, 0, gpMouseCursorOriginal, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
				}
			} while (ReturnCode != DD_OK);

			guiMouseBufferState = BUFFER_READY;
		}

		//
		// Check current state of the mouse cursor
		//

		if (show_mouse == FALSE)
		{
			if (guiMouseBufferState == BUFFER_READY)
			{
				show_mouse = TRUE;
			}
			else
			{
				show_mouse = FALSE;
			}
		}
		else
		{
			if (guiMouseBufferState == BUFFER_DISABLED)
			{
				show_mouse = FALSE;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////
		//
		// End of MOUSE_BUFFER_MUTEX
		//
		///////////////////////////////////////////////////////////////////////////////////////////////


		///////////////////////////////////////////////////////////////////////////////////////////////
		//
		// If fMouseState == TRUE
		//
		// (1) Save mouse background from gpBackBuffer to gpMouseCursorBackground
		// (2) If step (1) is successfull blit mouse cursor onto gpBackBuffer
		//
		///////////////////////////////////////////////////////////////////////////////////////////////

		if (show_mouse)
		{
			// Get the current mouse position
			POINT mouse_pos;
			GetCursorPos(&mouse_pos);
			// In window coords!
			ScreenToClient(m_hWnd, &mouse_pos);
			// Step (1) - Save mouse background
			Region.left	= mouse_pos.x - gsMouseCursorXOffset;
			Region.top	= mouse_pos.y - gsMouseCursorYOffset;
			Region.right	= Region.left + gusMouseCursorWidth;
			Region.bottom = Region.top + gusMouseCursorHeight;

			if (Region.right > usScreenWidth)
			{
				Region.right = usScreenWidth;
			}

			if (Region.bottom > usScreenHeight)
			{
				Region.bottom = usScreenHeight;
			}

			if ((Region.right > Region.left)&&(Region.bottom > Region.top))
			{
				//
				// Make sure the mouse background is marked for restore and coordinates are saved for the
				// future restore
				//

				gMouseCursorBackground[CURRENT_MOUSE_DATA].fRestore	= TRUE;
				gMouseCursorBackground[CURRENT_MOUSE_DATA].usRight	 = (INT16)Region.right - (INT16) Region.left;
				gMouseCursorBackground[CURRENT_MOUSE_DATA].usBottom	= (INT16)Region.bottom - (INT16) Region.top;
				if (Region.left < 0)
				{
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usLeft = (INT16) (0 - Region.left);
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseXPos = 0;
					Region.left = 0;
				}
				else
				{
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseXPos = (UINT16) mouse_pos.x - gsMouseCursorXOffset;
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usLeft = 0;
				}
				if (Region.top < 0)
				{
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseYPos = 0;
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usTop = (UINT16) (0 - Region.top);
					Region.top = 0;
				}
				else
				{
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseYPos = (UINT16) mouse_pos.y - gsMouseCursorYOffset;
					gMouseCursorBackground[CURRENT_MOUSE_DATA].usTop = 0;
				}

				if ((Region.right > Region.left)&&(Region.bottom > Region.top))
				{
					// Save clipped region
					gMouseCursorBackground[CURRENT_MOUSE_DATA].Region = Region;

					//
					// Ok, do the actual data save to the mouse background
					//

					do
					{
						ReturnCode = IDirectDrawSurface2_SGPBltFast(gMouseCursorBackground[CURRENT_MOUSE_DATA].pSurface, gMouseCursorBackground[CURRENT_MOUSE_DATA].usLeft, gMouseCursorBackground[CURRENT_MOUSE_DATA].usTop, gpBackBuffer, &Region, DDBLTFAST_NOCOLORKEY);
						if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
						{
							// Prevent the assertion failure lockup
							if (ReturnCode == DDERR_INVALIDRECT)
							{
								break;
							}

							DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
						}

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}
					} while (ReturnCode != DD_OK);

					//
					// Step (2) - Blit mouse cursor to back buffer
					//

					Region.left = gMouseCursorBackground[CURRENT_MOUSE_DATA].usLeft;
					Region.top = gMouseCursorBackground[CURRENT_MOUSE_DATA].usTop;
					Region.right = gMouseCursorBackground[CURRENT_MOUSE_DATA].usRight;
					Region.bottom = gMouseCursorBackground[CURRENT_MOUSE_DATA].usBottom;

					do
					{
						ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseXPos, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseYPos, gpMouseCursor, &Region, DDBLTFAST_SRCCOLORKEY);
						if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
						{
							// Prevent the assertion failure lockup
							if (ReturnCode == DDERR_INVALIDRECT)
							{
								break;
							}

							DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
						}

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}
					} while (ReturnCode != DD_OK);
				}
				else
				{
					//
					// Hum, the mouse was not blitted this round. Henceforth we will flag fRestore as FALSE
					//

					gMouseCursorBackground[CURRENT_MOUSE_DATA].fRestore = FALSE;
				}

			}
			else
			{
				//
				// Hum, the mouse was not blitted this round. Henceforth we will flag fRestore as FALSE
				//

				gMouseCursorBackground[CURRENT_MOUSE_DATA].fRestore = FALSE;

			}
		}
		else
		{
			//
			// Well since there was no mouse handling this round, we disable the mouse restore
			//

			gMouseCursorBackground[CURRENT_MOUSE_DATA].fRestore = FALSE;

		}



		///////////////////////////////////////////////////////////////////////////////////////////////
		// Rain																						//
		///////////////////////////////////////////////////////////////////////////////////////////////

		if( IsItAllowedToRenderRain() && gfProgramIsRunning )
		{
			BltVideoSurface( BACKBUFFER, guiRainRenderSurface, 0, 0, 0, VS_BLT_FAST | VS_BLT_USECOLORKEY, NULL );
			gfNextRefreshFullScreen = TRUE;
		}



		///////////////////////////////////////////////////////////////////////////////////////////////
		//
		// (1) Flip Pages
		// (2) If the page flipping worked, then we copy the contents of the primary surface back
		//	 to the backbuffer
		// (3) If step (2) was successfull we then restore the mouse background onto the backbuffer
		//	 if show_mouse is TRUE
		//
		///////////////////////////////////////////////////////////////////////////////////////////////

		//
		// Step (1) - Flip pages
		//
		Region.top = 0;
		Region.left = 0;
		Region.right = rcWindow.right - rcWindow.left;
		Region.bottom = rcWindow.bottom - rcWindow.top;
#endif
		// Windowed mode
		if(!m_Settings.is_full_screen())
		{
			m_pPrimarySurface->blit(*m_pBackBufferSurface, 0, 0, 0, nullptr);
/*
			do
			{
				ReturnCode = IDirectDrawSurface_Blt(
					gpPrimarySurface,		// dest surface
					&rcWindow,				// dest rect
					gpBackBuffer,			// src surface
					NULL,					// src rect (all of it)
					DDBLT_WAIT,
					NULL);
				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					// Prevent the minimizing bug
					if (ReturnCode == DDERR_INVALIDRECT)
					{
						return;
					}
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

					if (ReturnCode == DDERR_SURFACELOST)
					{
						goto ENDOFLOOP;
					}
				}
			} while (ReturnCode != DD_OK);
			gfRenderScroll = FALSE;
			gfScrollStart	= FALSE;
			guiDirtyRegionCount = 0;
			guiDirtyRegionExCount = 0;
			gfForceFullScreenRefresh = FALSE;
*/
		}
		else
		{
			m_pPrimarySurface->flip();
/*
			do
			{
				ReturnCode = IDirectDrawSurface_Flip(
					_gpPrimarySurface,
					NULL,
					gGameExternalOptions.gfVSync ? DDFLIP_WAIT : 0x00000008l
					);

				if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
				{
					// Prevent the minimizing bug
					if (ReturnCode == DDERR_INVALIDRECT)
					{
						return;
					}
					DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

					if (ReturnCode == DDERR_SURFACELOST)
					{
						goto ENDOFLOOP;
					}
				}
			} while (ReturnCode != DD_OK);
*/
#if 0
			//
			// Step (2) - Copy Primary Surface to the Back Buffer
			//

			if ( gfRenderScroll )
			{
				Region.left = 0;
				Region.top = 0;
				Region.right = gsVIEWPORT_END_X; //ods1 640;
				Region.bottom = gsVIEWPORT_END_Y;

				do
				{
					ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, 0, 0, gpPrimarySurface, &Region, DDBLTFAST_NOCOLORKEY);
					if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
					{
						DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}

					}
				} while (ReturnCode != DD_OK);

				//Get new background for mouse
				//
				// Ok, do the actual data save to the mouse background

				//


				gfRenderScroll = FALSE;
				gfScrollStart	= FALSE;

			}


			// COPY MOUSE AREAS FROM PRIMARY BACK!

			// FIRST OLD ERASED POSITION
			if (gMouseCursorBackground[PREVIOUS_MOUSE_DATA].fRestore == TRUE )
			{
				Region = 	gMouseCursorBackground[PREVIOUS_MOUSE_DATA].Region;

				do
				{
					ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gMouseCursorBackground[PREVIOUS_MOUSE_DATA].usMouseXPos, gMouseCursorBackground[PREVIOUS_MOUSE_DATA].usMouseYPos, gpPrimarySurface, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
					if (ReturnCode != DD_OK && ReturnCode != DDERR_WASSTILLDRAWING )
					{
						DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}
					}
				} while (ReturnCode != DD_OK);
			}

			// NOW NEW MOUSE AREA
			if (gMouseCursorBackground[CURRENT_MOUSE_DATA].fRestore == TRUE )
			{
				Region = 	gMouseCursorBackground[CURRENT_MOUSE_DATA].Region;

				do
				{
					ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseXPos, gMouseCursorBackground[CURRENT_MOUSE_DATA].usMouseYPos, gpPrimarySurface, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
					if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
					{
						DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}
					}
				} while (ReturnCode != DD_OK);
			}

			if (gfForceFullScreenRefresh == TRUE)
			{
				//
				// Method (1) - We will be refreshing the entire screen
				//
				Region.left = 0;
				Region.top = 0;
				Region.right = SCREEN_WIDTH;
				Region.bottom = SCREEN_HEIGHT;

				do
				{
					ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, 0, 0, gpPrimarySurface, &Region, DDBLTFAST_NOCOLORKEY);
					if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
					{
						DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}

					}
				} while (ReturnCode != DD_OK);

				guiDirtyRegionCount = 0;
				guiDirtyRegionExCount = 0;
				gfForceFullScreenRefresh = FALSE;
			}
			else
			{
				for (uiIndex = 0; uiIndex < guiDirtyRegionCount; uiIndex++)
				{
					Region.left	= gListOfDirtyRegions[uiIndex].iLeft;
					Region.top	= gListOfDirtyRegions[uiIndex].iTop;
					Region.right	= gListOfDirtyRegions[uiIndex].iRight;
					Region.bottom = gListOfDirtyRegions[uiIndex].iBottom;

					do
					{
						ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gListOfDirtyRegions[uiIndex].iLeft, gListOfDirtyRegions[uiIndex].iTop, gpPrimarySurface, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
						if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
						{
							DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
						}

						if (ReturnCode == DDERR_SURFACELOST)
						{
							goto ENDOFLOOP;
						}
					} while (ReturnCode != DD_OK);
				}

				guiDirtyRegionCount = 0;
				gfForceFullScreenRefresh = FALSE;

			}

			// Do extended dirty regions!
			for (uiIndex = 0; uiIndex < guiDirtyRegionExCount; uiIndex++)
			{
				Region.left	= gDirtyRegionsEx[uiIndex].iLeft;
				Region.top	= gDirtyRegionsEx[uiIndex].iTop;
				Region.right	= gDirtyRegionsEx[uiIndex].iRight;
				Region.bottom = gDirtyRegionsEx[uiIndex].iBottom;

				if ( ( Region.top < gsVIEWPORT_WINDOW_END_Y ) && gfRenderScroll )
				{
					continue;
				}

				do
				{
					ReturnCode = IDirectDrawSurface2_SGPBltFast(gpBackBuffer, gDirtyRegionsEx[uiIndex].iLeft, gDirtyRegionsEx[uiIndex].iTop, gpPrimarySurface, (LPRECT)&Region, DDBLTFAST_NOCOLORKEY);
					if ((ReturnCode != DD_OK)&&(ReturnCode != DDERR_WASSTILLDRAWING))
					{
						DirectXAttempt ( ReturnCode, __LINE__, __FILE__ );
					}

					if (ReturnCode == DDERR_SURFACELOST)
					{
						goto ENDOFLOOP;
					}
				} while (ReturnCode != DD_OK);
			}
#endif
		}
#if 0
		guiDirtyRegionExCount = 0;


ENDOFLOOP:
	}
#endif
	}

	void VideoManager::delete_primary_video_surfaces()
	{
		m_pPrimarySurface.reset();
		m_pBackBufferSurface.reset();
		m_pFrameBufferSurface.reset();
		m_pMouseBufferSurface.reset();
	}

	VideoSurface &VideoManager::get_main_surface(SurfaceType Type)
	{
		VideoSurface *p_ret = nullptr;
		switch(Type)
		{
		case ST_PRIMARY:
			p_ret = m_pPrimarySurface.get();
			break;
		case ST_BACK_BUFFER:
			p_ret = m_pBackBufferSurface.get();
			break;
		case ST_FRAME_BUFFER:
			p_ret = m_pFrameBufferSurface.get();
			break;
		case ST_MOUSE_BUFFER:
			p_ret = m_pMouseBufferSurface.get();
			break;
		default:
			throw std::logic_error("VideoManager: Unknown main surface type.");
			break;
		}
		JA2_ASSERT(p_ret != nullptr);

		return *p_ret;
	}

	VideoSurfacePtr_t VideoManager::create_surface(ImagePtr_t const &pImage, VideoSurface::MemoryUsage Flags, Image::TransparencyBlitFlag TransparentFlag)
	{
		// Create image from file
//x		ImagePtr_t p_image = m_ImageManager.load_image(Path, IMAGE_ALLIMAGEDATA);
		return  pImage->create_surface(m_pDirectDraw, Flags, TransparentFlag);

//x		JA2_DEBUG_MESSAGE(TOPIC_VIDEOSURFACE, DebugManager::DL_3, "Success in Creating Video Surface");
	}

	VideoSurfacePtr_t VideoManager::create_surface_from_dd_surface(IDirectDrawSurfacePtr pDDSurface)
	{
		// Get values based on DD Surface given
		DDSURFACEDESC2 dd_surface_desc = DDSURFACEDESC2();
		dd_surface_desc.dwSize = sizeof(dd_surface_desc);
		dx_attempt(pDDSurface->GetSurfaceDesc(&dd_surface_desc));
		// Get pixel format
		DDPIXELFORMAT const pixel_format = dd_surface_desc.ddpfPixelFormat;
		// Set flags
		VideoSurface::MemoryUsage mem_usage = VideoSurface::MU_DEFAULT;
		// Set memory flags
		if(dd_surface_desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY )
			mem_usage = VideoSurface::MU_SYSTEM;
		else if(dd_surface_desc.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY )
			mem_usage = VideoSurface::MU_VIDEO;

		return VideoSurfacePtr_t(new VideoSurface(static_cast<std::uint_least8_t>(pixel_format.dwRGBBitCount), VideoSurfaceHandlePtr_t(new VideoSurfaceHandle(pDDSurface, IDirectDrawSurfacePtr(), mem_usage, static_cast<std::uint_least16_t>(dd_surface_desc.dwWidth), static_cast<std::uint_least16_t>(dd_surface_desc.dwHeight)))));
	}

	void VideoManager::hide_cursor()
	{
	}
} /*sgp*/} /*ja2*/